#pragma once

#include <string>
#include <atomic>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <future>
#include <condition_variable>

#include "../net/socket_client.h"
#include "../service/rpc_service.h"
#include "../service/server_stub.h"
#include "../util/serial_util.h"

namespace lurpc {

/**
 * Process a rpc call in one tcp life cycle.
 */
template<typename T, typename ... Args>
T rpc_call(uint32_t service_id, const Args& ... _arg) {
    service_conf svc = rpc_services::get(service_id);

    std::string _args_buf = uint32_to_bytes(service_id);
    _args_buf.append(serialize(_arg...));

    std::string resp = socket_client::send_msg(svc._port, svc._host, _args_buf);

    return deserialize<T>(resp);
}

/**
 * Default message handler in server.
 * Call service with id(first 4 bytes in message) using parameters(rest part of message).
 */
std::string normal_server_handler(const std::string& _msg) {
    uint32_t _sid = *((uint32_t*)(_msg.c_str()));
    std::string r_msg = _msg.substr(4);
    auto _svc = server_stub::get_service(_sid);
    return _svc ? _svc(r_msg) : "[NON_SVC]";
}

/**
 * Start rpc server in specified port and host with message handler(default `normal_server_handler`)
 */
void start_rpc_server(int _port, const std::string& _host, std::function<std::string(const std::string&)> _handler = normal_server_handler) {
    socket_server(_port, _host, std::move(_handler)).start();
}

}
